﻿/*
** Xin YUAN, 2023, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////

//for SIMD

#pragma pack(push, 16)

//Vec2<T>

template <typename T>
class Vec2
{
public:
	typedef T  ValueType;

public:
	union {
#pragma pack(push, 1)
		struct { T x, y; } coord;
		struct { T g, a; } color;
		struct { T _1, _2; } v;
#pragma pack(pop)
		Vec<T, 2> data;
	};

public:
	Vec2() noexcept
	{
	}
	Vec2(const Vec2<T>& src) noexcept : coord(src.coord)
	{
	}
	Vec2(const T& _x, const T& _y) noexcept : coord{_x, _y}
	{
	}

	Vec2<T>& operator=(const Vec2<T>& src) noexcept
	{
		coord = src.coord;
		return *this;
	}

	Vec2<T>& Set(const T& _x, const T& _y) noexcept
	{
		coord.x = _x;
		coord.y = _y;
		return *this;
	}

	Vec2<T>& operator+=(const T& t) noexcept
	{
		coord.x += t;
		coord.y += t;
		return *this;
	}
	Vec2<T>& operator+=(const Vec2<T>& v) noexcept
	{
		coord.x += v.coord.x;
		coord.y += v.coord.y;
		return *this;
	}
	Vec2<T>& operator-=(const T& t) noexcept
	{
		coord.x -= t;
		coord.y -= t;
		return *this;
	}
	Vec2<T>& operator-=(const Vec2<T>& v) noexcept
	{
		coord.x -= v.coord.x;
		coord.y -= v.coord.y;
		return *this;
	}

	Vec2<T>& operator*=(const T& t) noexcept
	{
		coord.x *= t;
		coord.y *= t;
		return *this;
	}
	Vec2<T>& operator*=(const Vec2<T>& v) noexcept
	{
		coord.x *= v.coord.x;
		coord.y *= v.coord.y;
		return *this;
	}
	Vec2<T>& operator/=(const T& t) noexcept
	{
		coord.x /= t;
		coord.y /= t;
		return *this;
	}
	Vec2<T>& operator/=(const Vec2<T>& v) noexcept
	{
		coord.x /= v.coord.x;
		coord.y /= v.coord.y;
		return *this;
	}

	Vec2<T> operator+(const T& t) const noexcept
	{
		return Vec2<T>(coord.x + t, coord.y + t);
	}
	Vec2<T> operator+(const Vec2<T>& v) const noexcept
	{
		return Vec2<T>(coord.x + v.coord.x, coord.y + v.coord.y);
	}
	Vec2<T> operator-(const T& t) const noexcept
	{
		return Vec2<T>(coord.x - t, coord.y - t);
	}
	Vec2<T> operator-(const Vec2<T>& v) const noexcept
	{
		return Vec2<T>(coord.x - v.coord.x, coord.y - v.coord.y);
	}

	Vec2<T> operator*(const T& t) const noexcept
	{
		return Vec2<T>(coord.x * t, coord.y * t);
	}
	Vec2<T> operator*(const Vec2<T>& v) const noexcept
	{
		return Vec2<T>(coord.x * v.coord.x, coord.y * v.coord.y);
	}
	Vec2<T> operator/(const T& t) const noexcept
	{
		return Vec2<T>(coord.x / t, coord.y / t);
	}
	Vec2<T> operator/(const Vec2<T>& v) const noexcept
	{
		return Vec2<T>(coord.x / v.coord.x, coord.y / v.coord.y);
	}

	Vec2<T> operator-() const noexcept
	{
		return Vec2<T>(-coord.x, -coord.y);
	}

	T Norm() const noexcept
	{
		return SQUARE(coord.x) + SQUARE(coord.y);
	}
	T Length() const noexcept
	{
		return (T)FloatTraits<T>::sqrt(Norm());
	}
	Vec2<T>& Normalize() noexcept
	{
		T n = Length();
		coord.x /= n;
		coord.y /= n;
		return *this;
	}

	T Dot(const Vec2<T>& v) const noexcept
	{
		return coord.x * v.coord.x + coord.y * v.coord.y;
	}
	Vec2<T> Cross(const Vec2<T>& v) const noexcept
	{
		T cp = coord.x * v.coord.y - coord.y * v.coord.x;
		return Vec2<T>(cp, cp);
	}
};

template <typename T>
inline Vec2<T> operator+(const T& t, const Vec2<T>& v) noexcept
{
	return Vec2<T>(t + v.coord.x, t + v.coord.y);
}
template <typename T>
inline Vec2<T> operator-(const T& t, const Vec2<T>& v) noexcept
{
	return Vec2<T>(t - v.coord.x, t - v.coord.y);
}
template <typename T>
inline Vec2<T> operator*(const T& t, const Vec2<T>& v) noexcept
{
	return Vec2<T>(t * v.coord.x, t * v.coord.y);
}
template <typename T>
inline Vec2<T> operator/(const T& t, const Vec2<T>& v) noexcept
{
	return Vec2<T>(t / v.coord.x, t / v.coord.y);
}

//Vec3<T>

template <typename T>
class Vec3
{
public:
	typedef T  ValueType;

public:
	union {
#pragma pack(push, 1)
		struct { T x, y, z; } coord;
		struct { T r, g, b; } color;
		struct { T _1, _2, _3; } v;
#pragma pack(pop)
		Vec<T, 3> data;
	};

public:
	Vec3() noexcept
	{
	}
	Vec3(const Vec3<T>& src) noexcept : coord(src.coord)
	{
	}
	Vec3(const T& _x, const T& _y, const T& _z) noexcept : coord{_x, _y, _z}
	{
	}

	Vec3<T>& operator=(const Vec3<T>& src) noexcept
	{
		coord = src.coord;
		return *this;
	}

	Vec3<T>& Set(const T& _x, const T& _y, const T& _z) noexcept
	{
		coord.x = _x;
		coord.y = _y;
		coord.z = _z;
		return *this;
	}

	Vec3<T>& operator+=(const T& t) noexcept
	{
		coord.x += t;
		coord.y += t;
		coord.z += t;
		return *this;
	}
	Vec3<T>& operator+=(const Vec3<T>& v) noexcept
	{
		coord.x += v.coord.x;
		coord.y += v.coord.y;
		coord.z += v.coord.z;
		return *this;
	}
	Vec3<T>& operator-=(const T& t) noexcept
	{
		coord.x -= t;
		coord.y -= t;
		coord.z -= t;
		return *this;
	}
	Vec3<T>& operator-=(const Vec3<T>& v) noexcept
	{
		coord.x -= v.coord.x;
		coord.y -= v.coord.y;
		coord.z -= v.coord.z;
		return *this;
	}

	Vec3<T>& operator*=(const T& t) noexcept
	{
		coord.x *= t;
		coord.y *= t;
		coord.z *= t;
		return *this;
	}
	Vec3<T>& operator*=(const Vec3<T>& v) noexcept
	{
		coord.x *= v.coord.x;
		coord.y *= v.coord.y;
		coord.z *= v.coord.z;
		return *this;
	}
	Vec3<T>& operator/=(const T& t) noexcept
	{
		coord.x /= t;
		coord.y /= t;
		coord.z /= t;
		return *this;
	}
	Vec3<T>& operator/=(const Vec3<T>& v) noexcept
	{
		coord.x /= v.coord.x;
		coord.y /= v.coord.y;
		coord.z /= v.coord.z;
		return *this;
	}

	Vec3<T> operator+(const T& t) const noexcept
	{
		return Vec3<T>(coord.x + t, coord.y + t, coord.z + t);
	}
	Vec3<T> operator+(const Vec3<T>& v) const noexcept
	{
		return Vec3<T>(coord.x + v.coord.x, coord.y + v.coord.y, coord.z + v.coord.z);
	}
	Vec3<T> operator-(const T& t) const noexcept
	{
		return Vec3<T>(coord.x - t, coord.y - t, coord.z - t);
	}
	Vec3<T> operator-(const Vec3<T>& v) const noexcept
	{
		return Vec3<T>(coord.x - v.coord.x, coord.y - v.coord.y, coord.z - v.coord.z);
	}

	Vec3<T> operator*(const T& t) const noexcept
	{
		return Vec3<T>(coord.x * t, coord.y * t, coord.z * t);
	}
	Vec3<T> operator*(const Vec3<T>& v) const noexcept
	{
		return Vec3<T>(coord.x * v.coord.x, coord.y * v.coord.y, coord.z * v.coord.z);
	}
	Vec3<T> operator/(const T& t) const noexcept
	{
		return Vec3<T>(coord.x / t, coord.y / t, coord.z / t);
	}
	Vec3<T> operator/(const Vec3<T>& v) const noexcept
	{
		return Vec3<T>(coord.x / v.coord.x, coord.y / v.coord.y, coord.z / v.coord.z);
	}

	Vec3<T> operator-() const noexcept
	{
		return Vec3<T>(-coord.x, -coord.y, -coord.z);
	}

	T Norm() const noexcept
	{
		return SQUARE(coord.x) + SQUARE(coord.y) + SQUARE(coord.z);
	}
	T Length() const noexcept
	{
		return (T)FloatTraits<T>::sqrt(Norm());
	}
	Vec3<T>& Normalize() noexcept
	{
		T n = Length();
		coord.x /= n;
		coord.y /= n;
		coord.z /= n;
		return *this;
	}

	T Dot(const Vec3<T>& v) const noexcept
	{
		return coord.x * v.coord.x + coord.y * v.coord.y + coord.z * v.coord.z;
	}
	Vec3<T> Cross(const Vec3<T>& v) const noexcept
	{
		return Vec3<T>(coord.y * v.coord.z - coord.z * v.coord.y,
				coord.z * v.coord.x - coord.x * v.coord.z,
				coord.x * v.coord.y - coord.y * v.coord.x
				);
	}
};

template <typename T>
inline Vec3<T> operator+(const T& t, const Vec3<T>& v) noexcept
{
	return Vec3<T>(t + v.coord.x, t + v.coord.y, t + v.coord.z);
}
template <typename T>
inline Vec3<T> operator-(const T& t, const Vec3<T>& v) noexcept
{
	return Vec3<T>(t - v.coord.x, t - v.coord.y, t - v.coord.z);
}
template <typename T>
inline Vec3<T> operator*(const T& t, const Vec3<T>& v) noexcept
{
	return Vec3<T>(t * v.coord.x, t * v.coord.y, t * v.coord.z);
}
template <typename T>
inline Vec3<T> operator/(const T& t, const Vec3<T>& v) noexcept
{
	return Vec3<T>(t / v.coord.x, t / v.coord.y, t / v.coord.z);
}

//Vec4<T>

template <typename T>
class Vec4
{
public:
	typedef T  ValueType;

public:
	union {
#pragma pack(push, 1)
		struct { T x, y, z, w; } coord;
		struct { T r, g, b, a; } color;
		struct { T a, b, c, d; } plane;
		struct { T x, y, z, r; } sphere;
		struct { T _1, _2, _3, _4; } v;
#pragma pack(pop)
		Vec<T, 4> data;
	};

public:
	Vec4() noexcept
	{
	}
	Vec4(const Vec4<T>& src) noexcept : coord(src.coord)
	{
	}
	Vec4(const T& _x, const T& _y, const T& _z, const T& _w) noexcept : coord{_x, _y, _z, _w}
	{
	}

	Vec4<T>& operator=(const Vec4<T>& src) noexcept
	{
		coord = src.coord;
		return *this;
	}

	Vec4<T>& Set(const T& _x, const T& _y, const T& _z, const T& _w) noexcept
	{
		coord.x = _x;
		coord.y = _y;
		coord.z = _z;
		coord.w = _w;
		return *this;
	}

	Vec4<T>& operator+=(const T& t) noexcept
	{
		coord.x += t;
		coord.y += t;
		coord.z += t;
		coord.w += t;
		return *this;
	}
	Vec4<T>& operator+=(const Vec4<T>& v) noexcept
	{
		coord.x += v.coord.x;
		coord.y += v.coord.y;
		coord.z += v.coord.z;
		coord.w += v.coord.w;
		return *this;
	}
	Vec4<T>& operator-=(const T& t) noexcept
	{
		coord.x -= t;
		coord.y -= t;
		coord.z -= t;
		coord.w -= t;
		return *this;
	}
	Vec4<T>& operator-=(const Vec4<T>& v) noexcept
	{
		coord.x -= v.coord.x;
		coord.y -= v.coord.y;
		coord.z -= v.coord.z;
		coord.w -= v.coord.w;
		return *this;
	}

	Vec4<T>& operator*=(const T& t) noexcept
	{
		coord.x *= t;
		coord.y *= t;
		coord.z *= t;
		coord.w *= t;
		return *this;
	}
	Vec4<T>& operator*=(const Vec4<T>& v) noexcept
	{
		coord.x *= v.coord.x;
		coord.y *= v.coord.y;
		coord.z *= v.coord.z;
		coord.w *= v.coord.w;
		return *this;
	}
	Vec4<T>& operator/=(const T& t) noexcept
	{
		coord.x /= t;
		coord.y /= t;
		coord.z /= t;
		coord.w /= t;
		return *this;
	}
	Vec4<T>& operator/=(const Vec4<T>& v) noexcept
	{
		coord.x /= v.coord.x;
		coord.y /= v.coord.y;
		coord.z /= v.coord.z;
		coord.w /= v.coord.w;
		return *this;
	}

	Vec4<T> operator+(const T& t) const noexcept
	{
		return Vec4<T>(coord.x + t, coord.y + t, coord.z + t, coord.w + t);
	}
	Vec4<T> operator+(const Vec4<T>& v) const noexcept
	{
		return Vec4<T>(coord.x + v.coord.x, coord.y + v.coord.y, coord.z + v.coord.z, coord.w + v.coord.w);
	}
	Vec4<T> operator-(const T& t) const noexcept
	{
		return Vec4<T>(coord.x - t, coord.y - t, coord.z - t, coord.w - t);
	}
	Vec4<T> operator-(const Vec4<T>& v) const noexcept
	{
		return Vec4<T>(coord.x - v.coord.x, coord.y - v.coord.y, coord.z - v.coord.z, coord.w - v.coord.w);
	}

	Vec4<T> operator*(const T& t) const noexcept
	{
		return Vec4<T>(coord.x * t, coord.y * t, coord.z * t, coord.w * t);
	}
	Vec4<T> operator*(const Vec4<T>& v) const noexcept
	{
		return Vec4<T>(coord.x * v.coord.x, coord.y * v.coord.y, coord.z * v.coord.z, coord.w * v.coord.w);
	}
	Vec4<T> operator/(const T& t) const noexcept
	{
		return Vec4<T>(coord.x / t, coord.y / t, coord.z / t, coord.w / t);
	}
	Vec4<T> operator/(const Vec4<T>& v) const noexcept
	{
		return Vec4<T>(coord.x / v.coord.x, coord.y / v.coord.y, coord.z / v.coord.z, coord.w / v.coord.w);
	}

	Vec4<T> operator-() const noexcept
	{
		return Vec4<T>(-coord.x, -coord.y, -coord.z, -coord.w);
	}

	T Norm() const noexcept
	{
		return SQUARE(coord.x) + SQUARE(coord.y) + SQUARE(coord.z) + SQUARE(coord.w);
	}
	T Length() const noexcept
	{
		return (T)FloatTraits<T>::sqrt(Norm());
	}
	Vec4<T>& Normalize() noexcept
	{
		T n = Length();
		coord.x /= n;
		coord.y /= n;
		coord.z /= n;
		coord.w /= n;
		return *this;
	}

	T Dot(const Vec4<T>& v) const noexcept
	{
		return coord.x * v.coord.x + coord.y * v.coord.y + coord.z * v.coord.z + coord.w * v.coord.w;
	}
	Vec4<T> Cross(const Vec4<T>& v) const noexcept
	{
		return Vec4<T>(coord.y * v.coord.z - coord.z * v.coord.y,
				coord.z * v.coord.x - coord.x * v.coord.z,
				coord.x * v.coord.y - coord.y * v.coord.x,
				(T)0);
	}
};

template <typename T>
inline Vec4<T> operator+(const T& t, const Vec4<T>& v) noexcept
{
	return Vec4<T>(t + v.coord.x, t + v.coord.y, t + v.coord.z, t + v.coord.w);
}
template <typename T>
inline Vec4<T> operator-(const T& t, const Vec4<T>& v) noexcept
{
	return Vec4<T>(t - v.coord.x, t - v.coord.y, t - v.coord.z, t - v.coord.w);
}
template <typename T>
inline Vec4<T> operator*(const T& t, const Vec4<T>& v) noexcept
{
	return Vec4<T>(t * v.coord.x, t * v.coord.y, t * v.coord.z, t * v.coord.w);
}
template <typename T>
inline Vec4<T> operator/(const T& t, const Vec4<T>& v) noexcept
{
	return Vec4<T>(t / v.coord.x, t / v.coord.y, t / v.coord.z, t / v.coord.w);
}

#pragma pack(pop)

typedef Vec2<int32_t>   Vec2i;
typedef Vec2<uint32_t>  Vec2u;
typedef Vec2<float>     Vec2f;
typedef Vec2<double>    Vec2d;

typedef Vec3<int32_t>   Vec3i;
typedef Vec3<uint32_t>  Vec3u;
typedef Vec3<float>     Vec3f;
typedef Vec3<double>    Vec3d;

typedef Vec4<int32_t>   Vec4i;
typedef Vec4<uint32_t>  Vec4u;
typedef Vec4<float>     Vec4f;
typedef Vec4<double>    Vec4d;

////////////////////////////////////////////////////////////////////////////////
